/*
 * author M.Chang
 *
 * Copyright 2008 Ph.Bonnet and M.Chang
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

#include "ModelGenerator.h"


/*****************************************************************************/

/* constructor */
ModelGenerator::ModelGenerator(const std::string filename)
{
  /***************************************************************************
  ** Static data
  ****************************************************************************/
  filename_string = filename;

  /***************************************************************************/


  std::string buffer_string, template_string, template_filename;


  /* read base template from file */
  template_string.assign("");
  template_filename.assign(filename);
  template_filename.append("-common.mzn");

  std::ifstream common_file(template_filename.c_str(), std::ifstream::in);

  if (common_file.is_open())
  {
    while (!common_file.eof())
    {
      std::getline(common_file, buffer_string);
      template_string.append(buffer_string);
      template_string.append("\n");
    }
    common_file.close();
  }else
    std::cout << "Unable to open file" << std::endl;

  common_string.assign(template_string);


  /* read Mote template from file */
  template_string.assign("");
  template_filename.assign(filename);
  template_filename.append("-mote.mzn");

  std::ifstream mote_file(template_filename.c_str(), std::ifstream::in);

  if (mote_file.is_open())
  {
    while (!mote_file.eof())
    {
      std::getline(mote_file, buffer_string);
      template_string.append(buffer_string);
      template_string.append("\n");
    }
    mote_file.close();
  }else
    std::cout << "Unable to open file" << std::endl;

  mote_string.assign(template_string);

}


/* destructor */
ModelGenerator::~ModelGenerator()
{
//  std::cout << "ModelGenerator destructor called" << std::endl;

/*
  std::vector<Mote*>::iterator Mote_vector_it;

  for (Mote_vector_it = Mote_vector.begin();
       Mote_vector_it != Mote_vector.end();
       Mote_vector_it++)
  {
    delete *Mote_vector_it;
  }
*/
}


/*****************************************************************************/

void ModelGenerator::addMote(Mote& Mote)
{
  if (!Mote.empty())
    Mote_vector.push_back(Mote);
}

void ModelGenerator::setEpoch(unsigned int ep)
{
  epoch = ep;
}

unsigned int ModelGenerator::getEpoch()
{
  return epoch;
}

void ModelGenerator::setEnergy(unsigned int en)
{
  energy = en;
}

/*****************************************************************************/

bool ModelGenerator::getModel(std::string& model, std::map<std::string, vector_string>& route_map, std::map<std::string, vector_string>& neighbor_map)
{

  if (Mote_vector.empty())
  {
    return false;
  }

  std::string max_reward_string = "(";
  std::string max_energy_string = "(";
  std::string max_time_string = "(";

/*
  std::string destination = "__Mote__";
  std::string source = "Temperature";
  replace(base, source, destination);
*/
//  std::map<std::string, std::string> transmit_map;
//  std::map<std::string, std::string> bandwidth_map;

  std::map<std::string, std::string> relay_energy_map;
  std::map<std::string, std::string> relay_time_map;

  std::map<std::string, std::string> pre_relay_energy_map;
  std::map<std::string, std::string> pre_relay_time_map;

  std::map<std::string, std::string> neighbor_energy_map;
  std::map<std::string, std::string> neighbor_time_map;

  std::map<std::string, std::string> pre_neighbor_energy_map;
  std::map<std::string, std::string> pre_neighbor_time_map;

  /* build transmit cost pr node */
  std::vector<Mote>::iterator Mote_vector_it;

  for (Mote_vector_it = Mote_vector.begin();
       Mote_vector_it < Mote_vector.end();
       Mote_vector_it++)
  {
    std::map<std::string, std::string> tokens_map;
    Mote_vector_it->getEventMap(tokens_map);

    std::string transmit_string;
    transmit_string.assign("sum([");
    transmit_string.append(tokens_map["__Mote__"]);
    transmit_string.append("TransmitEnergy[s] * bool2int(");
    transmit_string.append(tokens_map["__Mote__"]);
    transmit_string.append("Selected[s]) | s in ");
    transmit_string.append(tokens_map["__Mote__"]);
    transmit_string.append("Sensors])\n");
    pre_relay_energy_map[tokens_map["__Mote__"]] = transmit_string;

    std::string bandwidth_string;
    bandwidth_string.assign("sum([");
    bandwidth_string.append(tokens_map["__Mote__"]);
    bandwidth_string.append("TransmitTime[s] * bool2int(");
    bandwidth_string.append(tokens_map["__Mote__"]);
    bandwidth_string.append("Selected[s]) | s in ");
    bandwidth_string.append(tokens_map["__Mote__"]);
    bandwidth_string.append("Sensors])\n");
    pre_relay_time_map[tokens_map["__Mote__"]] = bandwidth_string;

    std::string neighbor_energy_string;
    neighbor_energy_string.assign("sum([");
    neighbor_energy_string.append(tokens_map["__Mote__"]);
    neighbor_energy_string.append("NeighborEnergy[s] * bool2int(");
    neighbor_energy_string.append(tokens_map["__Mote__"]);
    neighbor_energy_string.append("Selected[s]) | s in ");
    neighbor_energy_string.append(tokens_map["__Mote__"]);
    neighbor_energy_string.append("Sensors])\n");
    pre_neighbor_energy_map[tokens_map["__Mote__"]] = neighbor_energy_string;

    std::string neighbor_time_string;
    neighbor_time_string.assign("sum([");
    neighbor_time_string.append(tokens_map["__Mote__"]);
    neighbor_time_string.append("NeighborTime[s] * bool2int(");
    neighbor_time_string.append(tokens_map["__Mote__"]);
    neighbor_time_string.append("Selected[s]) | s in ");
    neighbor_time_string.append(tokens_map["__Mote__"]);
    neighbor_time_string.append("Sensors])\n");
    pre_neighbor_time_map[tokens_map["__Mote__"]] = neighbor_time_string;
  }

  for (Mote_vector_it = Mote_vector.begin();
       Mote_vector_it < Mote_vector.end();
       Mote_vector_it++)
  {
    std::map<std::string, std::string> tokens_map;
    Mote_vector_it->getEventMap(tokens_map);


    std::vector<std::string> children_vector = route_map[tokens_map["__Mote__"]];
    std::vector<std::string>::iterator children_vector_it;

    std::string relay_string;
    std::string bandwidth_string;

    for (children_vector_it = children_vector.begin();
         children_vector_it < children_vector.end();
         children_vector_it++)
    {
      relay_string.append(pre_relay_energy_map[*children_vector_it]);
      relay_string.append("+");

      bandwidth_string.append(pre_relay_time_map[*children_vector_it]);
      bandwidth_string.append("+");
    }

    relay_string.append("0");
    relay_energy_map[tokens_map["__Mote__"]] = relay_string;

    bandwidth_string.append("0");
    relay_time_map[tokens_map["__Mote__"]] = bandwidth_string;


    std::vector<std::string> neighbor_vector = neighbor_map[tokens_map["__Mote__"]];
    std::vector<std::string>::iterator neighbor_vector_it;

    std::string neighbor_time_string;
    std::string neighbor_energy_string;

    for (neighbor_vector_it = neighbor_vector.begin();
         neighbor_vector_it < neighbor_vector.end();
         neighbor_vector_it++)
    {
      neighbor_time_string.append(pre_neighbor_time_map[*neighbor_vector_it]);
      neighbor_time_string.append("+");

      neighbor_energy_string.append(pre_neighbor_energy_map[*neighbor_vector_it]);
      neighbor_energy_string.append("+");
    }
    neighbor_time_string.append("0");
    neighbor_time_map[tokens_map["__Mote__"]] = neighbor_time_string;

    neighbor_energy_string.append("0");
    neighbor_energy_map[tokens_map["__Mote__"]] = neighbor_energy_string;
  }

  /* build Mote */
  for (Mote_vector_it = Mote_vector.begin();
       Mote_vector_it < Mote_vector.end();
       Mote_vector_it++)
  {
    std::map<std::string, std::string> tokens_map;
    Mote_vector_it->getEventMap(tokens_map);

    max_reward_string.append(tokens_map["__max_node_reward__"]);
    max_reward_string.append(" + ");

    max_energy_string.append(tokens_map["__max_node_energy__"]);
    max_energy_string.append(" + ");

    max_time_string.append(tokens_map["__max_node_time__"]);
    max_time_string.append(" + ");

    std::string base;

    /* check if custom Mote file exists */
    std::string custom_string, buffer_string, custom_filename;
    custom_filename.assign(filename_string);
    custom_filename.append("-");
    custom_filename.append(tokens_map["__Mote__"]);
    custom_filename.append(".mzn");

    std::ifstream custom_file(custom_filename.c_str(), std::ifstream::in);

    if (custom_file.is_open())
    {
      while (!custom_file.eof())
      {
        std::getline(custom_file, buffer_string);
        custom_string.append(buffer_string);
        custom_string.append("\n");
      }
      custom_file.close();

      base = custom_string;
    }
    else
    {
      base = mote_string;
    }

    /* search and replace tokens in Mote string */
    std::map<std::string, std::string>::iterator tokens_map_it;
    for (tokens_map_it = tokens_map.begin();
         tokens_map_it != tokens_map.end();
         tokens_map_it++)
    {
      replace(base, (*tokens_map_it).second, (*tokens_map_it).first);
//      std::cout << (*tokens_map_it).second << " " << (*tokens_map_it).first << std::endl;
    }

    replace(base, relay_energy_map[tokens_map["__Mote__"]], "__relay_energy__");
    replace(base, relay_time_map[tokens_map["__Mote__"]], "__relay_time__");

    replace(base, neighbor_energy_map[tokens_map["__Mote__"]], "__neighbor_energy__");
    replace(base, neighbor_time_map[tokens_map["__Mote__"]], "__neighbor_time__");

    model.append(base);
  }

  max_reward_string.append("0)");
  max_energy_string.append("0)");
  max_time_string.append("0)");

  /* build base */
  std::string nodetime_string = "(";
  std::string nodeenergy_string = "(";
  std::string nodereward_string = "(";
  std::string nodeutilization_string = "(";
  std::string int_solve_order_string = "(";
  std::string bool_solve_order_string = "(";
  std::string output_string = "[ \"<begin>\\n\",\n";

  Mote_vector_it = Mote_vector.begin();
  std::string Mote_name;
  Mote_vector_it->getName(Mote_name);

  nodetime_string.append(Mote_name);
  nodetime_string.append("NodeTime");
  nodeenergy_string.append(Mote_name);
  nodeenergy_string.append("NodeEnergy");

  nodereward_string.append(Mote_name);
  nodereward_string.append("NodeReward");
  nodeutilization_string.append(Mote_name);
  nodeutilization_string.append("NodeUtilization");

  int_solve_order_string.append(Mote_name);
  int_solve_order_string.append("SensorFrequency");
  bool_solve_order_string.append(Mote_name);
  bool_solve_order_string.append("Selected");

  output_string.append("  \"");
  output_string.append(Mote_name);
  output_string.append(":\",\n");
  output_string.append("  show(");
  output_string.append(Mote_name);
  output_string.append("Selected), \";\",\n");
//  output_string.append("  show(");
//  output_string.append(Mote_name);
//  output_string.append("Domain), \";\\n\",\n");
  output_string.append("  show(");
  output_string.append(Mote_name);
  output_string.append("SensorFrequency), \";\",\n");
  output_string.append("  show(");
  output_string.append(Mote_name);
  output_string.append("NodeEnergy), \";\",\n");
  output_string.append("  show(");
  output_string.append(Mote_name);
  output_string.append("NodeTime), \";\\n\",\n");
//  output_string.append("  show(");
//  output_string.append(Mote_name);
//  output_string.append("Plan), \";\\n\",\n");

  for (Mote_vector_it++;
       Mote_vector_it < Mote_vector.end();
       Mote_vector_it++)
  {
    Mote_vector_it->getName(Mote_name);

    nodetime_string.append(" + ");
    nodetime_string.append(Mote_name);
    nodetime_string.append("NodeTime");
    nodeenergy_string.append(" + ");
    nodeenergy_string.append(Mote_name);
    nodeenergy_string.append("NodeEnergy");

    nodereward_string.append(" + ");
    nodereward_string.append(Mote_name);
    nodereward_string.append("NodeReward");
    nodeutilization_string.append(" + ");
    nodeutilization_string.append(Mote_name);
    nodeutilization_string.append("NodeUtilization");

    int_solve_order_string.append(" ++ ");
    int_solve_order_string.append(Mote_name);
    int_solve_order_string.append("SensorFrequency");
    bool_solve_order_string.append(" ++ ");
    bool_solve_order_string.append(Mote_name);
    bool_solve_order_string.append("Selected");

    output_string.append("\n  \"");
    output_string.append(Mote_name);
    output_string.append(":\",\n");
    output_string.append("  show(");
    output_string.append(Mote_name);
    output_string.append("Selected), \";\",\n");
//    output_string.append("  show(");
//    output_string.append(Mote_name);
//    output_string.append("Domain), \";\\n\",\n");
    output_string.append("  show(");
    output_string.append(Mote_name);
    output_string.append("SensorFrequency), \";\",\n");
    output_string.append("  show(");
    output_string.append(Mote_name);
    output_string.append("NodeEnergy), \";\",\n");
    output_string.append("  show(");
    output_string.append(Mote_name);
    output_string.append("NodeTime), \";\\n\",\n");
//    output_string.append("  show(");
//    output_string.append(Mote_name);
//    output_string.append("Plan), \";\\n\",\n");
  }

  nodetime_string.append(")");
  nodeenergy_string.append(")");
  nodereward_string.append(")");
  nodeutilization_string.append(")");
  int_solve_order_string.append(")");
  bool_solve_order_string.append(")");

    output_string.append("  show(");
    output_string.append(nodereward_string);
    output_string.append("), \";\\n\",\n");
//    output_string.append("  show(");
//    output_string.append(nodetime_string);
//    output_string.append("), \";\\n\",\n");
    output_string.append("  show(");
    output_string.append(nodeenergy_string);
    output_string.append("), \";\\n\",\n");
  output_string.append("\n\"<end>\\n\" ]");

  std::string base = common_string;

  replace(base, nodetime_string, "__node_time__");
  replace(base, nodeenergy_string, "__node_energy__");
  replace(base, nodereward_string, "__node_reward__");
  replace(base, nodeutilization_string, "__node_utilization__");
  replace(base, output_string, "__output__");
  replace(base, max_reward_string, "__max_node_reward__");
  replace(base, max_energy_string, "__max_node_energy__");
  replace(base, max_time_string, "__max_node_time__");
  replace(base, int_solve_order_string, "__int_solve_order__");
  replace(base, bool_solve_order_string, "__bool_solve_order__");

  std::stringstream ss;
  ss << Mote_vector.size();
  replace(base, ss.str(), "__number_of_modalities__");

  ss.str("");
  ss << epoch;
  replace(base, ss.str(), "__epoch__");

  ss.str("");
  ss << energy;
  replace(base, ss.str(), "__energy_constraint__");

  model.append(base);

  return true;
}
/*
  tokens_map["__number_of_sensors__"] = "3";
  tokens_map["__domain_set__"] = "{ 1, 2, 3 }";
  tokens_map["__domain__"] = "[| 1, 2, 3 | 1, 2, 3 | 1, 2, 3 |]";
  tokens_map["__reward_table__"] = "[| 10, 100, 21 | 100, 20, 21 | 100, 20, 21 |]";
  tokens_map["__energy_table__"] = "[| 1, 1, 1 | 1, 1, 1 | 1, 1, 1 |]";
  tokens_map["__time_table__"] = "[| 1, 1, 1 | 1, 1, 1 | 1, 1, 1 |]";
  tokens_map["__group__"] = "[| 1, 1, 1 | 1, 1, 1 | 1, 1, 1 |]";
  tokens_map["__frequency_group__"] = "[| {1, 3}, {2, 4}, {} | {1, 3}, {2, 4}, {} | {1, 3}, {2, 4}, {} |]";
*/


void ModelGenerator::replace(std::string& base_string, const std::string& source_string, const std::string& destination_string)
{
  size_t pos = base_string.find(destination_string, 0);
  while (pos != std::string::npos)
  {
    base_string.replace(pos, destination_string.size(), source_string);

    pos = base_string.find(destination_string, pos);
  };
}


